﻿/* 
 * Evo
 * 
 * Marcin Krupowicz (marcin@krupowicz.eu)
 * Barbara Szymanek (basia.szymanek@gmail.com)
 * 
 * January, 2009
 *
 */

using System;
using System.Collections.Generic;
using System.Text;

namespace Evo.Operators
{
	public abstract class ReplacementOperator<T> : IReplacementOperator<T> where T : IIndividual
	{
		protected int quantity;
		protected bool onlyChildren;

		public ReplacementOperator(int quantity, bool childrenOnly)
		{
			this.quantity = quantity;
			this.onlyChildren = childrenOnly;
		}

		/// <summary>
		/// Get population from which Replacement operator should get individuals to the new population
		/// </summary>
		/// <param name="parents">Parents' population</param>
		/// <param name="children">Children's population</param>
		/// <param name="onlyChildren">If true, selectes only from children's population. 
		/// If false, from sum of parents' and children's populations</param>
		/// <returns>List of individuals</returns>
		protected Population<T> GetApropriatePopulation(Population<T> parents, Population<T> children)
		{
			List<T> individuals = new List<T>();

			if (this.onlyChildren)
			{
				if (this.quantity > children.Size)
					throw new Exception("quantity > children.Size");
			}
			else
			{
				if (this.quantity > children.Size + parents.Size)
					throw new Exception("quantity > children.Size + parents.Size");
			}

			if (this.onlyChildren)
				individuals = children.Individuals;
			else
			{
				individuals = children.Individuals; //NOTE: possibly buggy
				individuals.AddRange(parents.Individuals);
			}
			return new Population<T>(individuals);
		}

		public abstract Population<T> Replace(Population<T> parents, Population<T> children, Population<T> oldPopulation);
	}

    public class RouletteReplacementOperator<T> : ReplacementOperator<T> where T:IIndividual
    {
        public RouletteReplacementOperator(int quantity, bool onlyChildren)
			:base(quantity, onlyChildren)
        {
        }

        public  override Population<T> Replace(Population<T> parents, Population<T> children, Population<T> oldPopulation)
        {
			Population<T> unionPopulation = GetApropriatePopulation(parents, children);
			Population<T> toReplace = Selection<T>.RouletteSelection(unionPopulation, this.quantity);
			oldPopulation.Individuals.Sort();

			for (int i = oldPopulation.Size - 1, j = 0; j < toReplace.Size; j++, i--)
				oldPopulation[i] = toReplace[j];

			return oldPopulation;
        }

        public override string ToString()
        {
            return String.Format("RouletteReplacementOperator\n\tquantity: {0}\n\tonlyChildren: {1}",
                quantity, onlyChildren);
        }
    }

	public class TournamentReplacementOperator<T> : ReplacementOperator<T> where T : IIndividual
	{
		private int tournamentSize;
		private int winnersNumber;

		public TournamentReplacementOperator(int quantity, bool onlyChildren, int tournamentSize, int winnersNumber)
			: base(quantity, onlyChildren)
		{
			this.tournamentSize = tournamentSize;
			this.winnersNumber = winnersNumber;
		}

		public override Population<T> Replace(Population<T> parents, Population<T> children, Population<T> oldPopulation)
		{
			Population<T> unionPopulation = GetApropriatePopulation(parents, children);
			if (this.tournamentSize > unionPopulation.Size)
				throw new Exception("tournamentSize > unionPopulation.Size");
			Population<T> toReplace = Selection<T>.TournamentSelection(unionPopulation, this.quantity, this.tournamentSize, this.winnersNumber);
			oldPopulation.Individuals.Sort();

			for (int i = oldPopulation.Size - 1, j = 0; j < toReplace.Size; j++, i--)
				oldPopulation[i] = toReplace[j];

			return oldPopulation;
		}

        public override string ToString()
        {
            return String.Format("TournamentReplacementOperator\n\tquantity: {0}\n\tonlyChildren: {1}\n\ttournament size: {2}\n\twinner numbers: {3}",
                quantity, onlyChildren, tournamentSize, winnersNumber);
        }
	}

	public class KBestReplacementOperator<T> : ReplacementOperator<T> where T:IIndividual
	{
		/// <param name="quantity">number of individuals to replace</param>
		/// <param name="onlyChildren">If true, selectes only from children's population. 
		/// If false, from sum of parents' and children's populations</param>
		/// <param name="elitism">if true, elitism os on. Off otherwise</param>
		public KBestReplacementOperator(int quantity, bool onlyChildren)
			: base(quantity, onlyChildren)
		{
		}

		/// <param name="parents">parents' population</param>
		/// <param name="children">children's population</param>
		/// <param name="oldPopulation">population on which replacement will be performed</param>
		/// <returns>population with replaced individuals</returns>
		public override Population<T> Replace(Population<T> parents, Population<T> children, Population<T> oldPopulation)
		{
			if (quantity > oldPopulation.Size)
				throw new Exception("Quantity is bigger than old population size");

			Population<T> unionPopulation = GetApropriatePopulation(parents, children);
			Population<T> toReplace = Selection<T>.KBestSelection(unionPopulation, this.quantity);
			oldPopulation.Individuals.Sort();
			for (int i = oldPopulation.Size - 1, j = 0; j < toReplace.Size; j++, i--)
				oldPopulation[i] = toReplace[j];

			return oldPopulation;
		}

        public override string ToString()
        {
            return String.Format("KBestReplacementOperator\n\tquantity: {0}\n\tonlyChildren: {1}",
               quantity, onlyChildren);    
        }
	}


	//public class Replacement<T> where T : IIndividual
	//{
	//    public static Population<T> BlockReplacement(Population<T> parents, Population<T> children, int quantity, 
	//        bool onlyChildren, bool elitism)
	//    {
	//        Population<T> population = GetApropriatePopulation(parents, children, onlyChildren);

	//        if ((elitism && quantity - 1 < population.Size) || (!elitism && quantity < population.Size))
	//            throw new Exception("Quantity of new population is too big");

	//        Population<T> newPopulation = Selection<T>.KBestSelection(population, elitism ? quantity - 1 : quantity);
	//        if (elitism)
	//            newPopulation.Individuals.Add(parents.Best);
	//        return newPopulation;
	//    }


	//    public static Population<T> RouletteReplacement(Population<T> parents, Population<T> children, int quantity,
	//        bool onlyChildren, bool elitism)
	//    {
	//        Population<T> population = GetApropriatePopulation(parents, children, onlyChildren);
	//        Population<T> newPopulation = Selection<T>.RouletteSelection(population, elitism ? quantity - 1 : quantity);
			
	//        if (elitism)
	//            newPopulation.Individuals.Add(parents.Best);
			
	//        return newPopulation;
	//    }

	//    public static Population<T> TournamentReplacement(Population<T> parents, Population<T> children, int quantity, 
	//        int tournamentSize, int winnerNumber, bool onlyChildren, bool elitism)
	//    {
	//        Population<T> population = GetApropriatePopulation(parents, children, onlyChildren);
	//        Population<T> newPopulation = Selection<T>.TournamentSelection(population, elitism ? quantity - 1 : quantity,
	//            tournamentSize, winnerNumber);

	//        if (elitism)
	//            newPopulation.Individuals.Add(parents.Best);
	//        return newPopulation;
	//    }
	//}
}
